import 'dart:collection';
import 'dart:math';
import 'dart:ui';

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

/// @Author 杨璐铭
/// @Date 2023/6/3 4:42 PM
/// @describe 处理键盘遮挡输入框case
class AvoidKeyboard extends StatefulWidget {
  AvoidKeyboard({
    super.key,
    required this.child,
    this.duration = const Duration(milliseconds: 100),
    this.curve = Curves.easeOut,
    this.autoScroll = false,
    this.focusPadding = 12.0,
  }) : assert(child is ScrollView ? child.controller != null : true);

  final Widget child;

  final Duration duration;

  final Curve curve;

  final bool autoScroll;

  final double focusPadding;

  @override
  State<AvoidKeyboard> createState() => _AvoidKeyboardState();
}

class _AvoidKeyboardState extends State<AvoidKeyboard> with WidgetsBindingObserver {
  final _animationKey = GlobalKey<ImplicitlyAnimatedWidgetState>();
  Function(AnimationStatus)? _animationListener;
  ScrollController? _scrollController;
  double _overlap = 0.0;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    if (_animationListener != null) {
      _animationKey.currentState?.animation.removeStatusListener(_animationListener!);
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_animationListener == null) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _animationListener = _animationStatusChanged;
        _animationKey.currentState?.animation.addStatusListener(_animationStatusChanged);
      });
    }

    if (widget.child is ScrollView) {
      var scrollView = widget.child as ScrollView;
      _scrollController = scrollView.controller;
      return _buildAnimatedContainer(widget.child);
    }

    if (widget.autoScroll) {
      _scrollController = ScrollController();
      return _buildAnimatedContainer(LayoutBuilder(
        builder: (context, constraints) {
          return SingleChildScrollView(
            controller: _scrollController,
            child: ConstrainedBox(
              constraints: BoxConstraints(
                minHeight: constraints.maxHeight,
              ),
              child: widget.child,
            ),
          );
        },
      ));
    }

    return _buildAnimatedContainer(widget.child);
  }

  @override
  void didChangeMetrics() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _resize();
    });
  }

  void _animationStatusChanged(AnimationStatus status) {
    if (status == AnimationStatus.completed) {
      final keyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0.0;
      if (keyboardVisible) {
        _keyboardShown();
      }
    }
  }

  Widget _buildAnimatedContainer(Widget child) {
    return AnimatedContainer(
      key: _animationKey,
      padding: EdgeInsets.only(bottom: max(1, _overlap)),
      duration: widget.duration,
      curve: widget.curve,
      child: child,
    );
  }

  void _resize() {
    if (!mounted) {
      return;
    }

    final object = context.findRenderObject();
    if (object == null || object is! RenderBox || !object.hasSize) {
      // 如果 RenderObject 不存在、不是 RenderBox 或尚未完成布局，直接返回
      return;
    }
    final box = object;
    final offset = box.localToGlobal(Offset.zero);

    if (offset.dx.isNaN || offset.dy.isNaN) {
      // 如果 offset 计算结果无效，直接返回
      return;
    }
    final widgetRect = Rect.fromLTWH(
      offset.dx,
      offset.dy,
      box.size.width,
      box.size.height,
    );

    final double viewInsetsBottom = EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio).bottom;
    final mediaQuery = MediaQuery.of(context);
    final screenSize = mediaQuery.size;
    final keyboardTop = screenSize.height - viewInsetsBottom;

    if (widgetRect.top > keyboardTop) {
      return;
    }

    final overlap = max(0.0, widgetRect.bottom - keyboardTop);
    if (overlap != _overlap) {
      setState(() {
        _overlap = overlap;
      });
    }
  }

  void _keyboardShown() {
    if (!widget.autoScroll) {
      return;
    }
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _scrollToFocusedObject();
    });
  }

  void _scrollToFocusedObject() {
    final focused = _findFocusedObject(context.findRenderObject());
    if (focused == null) return;
    _scrollToObject(focused);
  }

  RenderObject? _findFocusedObject(RenderObject? root) {
    if (root == null) return null;
    final q = Queue<RenderObject>();
    q.add(root);
    while (q.isNotEmpty) {
      final node = q.removeFirst();
      if (node is RenderEditable && node.hasFocus) {
        return node;
      }
      node.visitChildren((child) {
        q.add(child);
      });
    }
    return null;
  }

  void _scrollToObject(RenderObject object) {
    final viewport = RenderAbstractViewport.of(object);
    final offset = viewport.getOffsetToReveal(object, 1.0).offset + widget.focusPadding;

    if (_scrollController == null) return;

    if (offset > _scrollController!.position.pixels) {
      _scrollController!.position.moveTo(
        offset,
        duration: widget.duration,
        curve: widget.curve,
      );
    }
  }
}
